<?php

namespace Dongasai\WkProtoPhp\Packets;

use Dongasai\WkProtoPhp\Enums\FrameType;
use Dongasai\WkProtoPhp\Framer;
use Dongasai\WkProtoPhp\Encoder;
use Dongasai\WkProtoPhp\Decoder;
use Dongasai\WkProtoPhp\Enums\Setting;
use Dongasai\WkProtoPhp\Enums\ChannelType;
use Dongasai\WkProtoPhp\JsonSerializableInterface;
use Dongasai\WkProtoPhp\JsonSerializableTrait;

/**
 * SEND发送包
 */
class SendPacket extends Framer implements JsonSerializableInterface
{
    use JsonSerializableTrait;
    
    private Setting $setting;
    private string $msgKey = '';
    private int $expire = 0;
    private int $clientSeq = 0;
    private string $clientMsgNo = '';
    private string $streamNo = '';
    private string $channelId = '';
    private int $channelType = ChannelType::PERSON->value;
    private string $topic = '';
    private string $payload = '';

    public function __construct()
    {
        parent::__construct(FrameType::SEND->value);
        $this->setting = new Setting();
    }

    public function getFrameType(): int
    {
        return FrameType::SEND->value;
    }

    public function __toString(): string
    {
        return sprintf(
            'SEND Setting:%s MsgKey:%s Expire:%d ClientSeq:%d ClientMsgNo:%s ChannelId:%s ChannelType:%s Topic:%s Payload:%s',
            $this->setting,
            $this->msgKey,
            $this->expire,
            $this->clientSeq,
            $this->clientMsgNo,
            $this->channelId,
            ChannelType::toString($this->channelType),
            $this->topic,
            $this->payload
        );
    }

    public function uniqueKey(): string
    {
        return sprintf('%s-%d-%s-%d', $this->channelId, $this->channelType, $this->clientMsgNo, $this->clientSeq);
    }

    public function verityString(): string
    {
        return sprintf('%d%s%s%d%s', $this->clientSeq, $this->clientMsgNo, $this->channelId, $this->channelType, $this->payload);
    }

    // Getters and Setters
    public function getSetting(): Setting
    {
        return $this->setting;
    }

    public function setSetting(Setting|int $setting): void
    {
        if ($setting instanceof Setting) {
            $this->setting = $setting;
        } else {
            $this->setting = new Setting($setting);
        }
    }

    public function getMsgKey(): string
    {
        return $this->msgKey;
    }

    public function setMsgKey(string $msgKey): void
    {
        $this->msgKey = $msgKey;
    }

    public function getExpire(): int
    {
        return $this->expire;
    }

    public function setExpire(int $expire): void
    {
        $this->expire = $expire;
    }

    public function getClientSeq(): int
    {
        return $this->clientSeq;
    }

    public function setClientSeq(int $clientSeq): void
    {
        $this->clientSeq = $clientSeq;
    }

    public function getClientMsgNo(): string
    {
        return $this->clientMsgNo;
    }

    public function setClientMsgNo(string $clientMsgNo): void
    {
        $this->clientMsgNo = $clientMsgNo;
    }

    public function getStreamNo(): string
    {
        return $this->streamNo;
    }

    public function setStreamNo(string $streamNo): void
    {
        $this->streamNo = $streamNo;
    }

    public function getChannelId(): string
    {
        return $this->channelId;
    }

    public function setChannelId(string $channelId): void
    {
        $this->channelId = $channelId;
    }

    public function getChannelType(): int
    {
        return $this->channelType;
    }

    public function setChannelType(int $channelType): void
    {
        $this->channelType = $channelType;
    }

    public function getTopic(): string
    {
        return $this->topic;
    }

    public function setTopic(string $topic): void
    {
        $this->topic = $topic;
    }

    public function getPayload(): string
    {
        // 如果payload是二进制数据但包含可读文本，尝试转换
        if (is_string($this->payload) && preg_match('~^[\x20-\x7E\x0A\x0D\x09]+$~', $this->payload)) {
            return $this->payload;
        }
        // 尝试从二进制数据中提取UTF-8字符串
        if (is_string($this->payload)) {
            // 检查是否是有效的UTF-8
            if (mb_check_encoding($this->payload, 'UTF-8')) {
                return $this->payload;
            }
            // 尝试清理可能的二进制前缀
            $cleaned = preg_replace('~^[\x00-\x1F]+~', '', $this->payload);
            if ($cleaned && mb_check_encoding($cleaned, 'UTF-8')) {
                return $cleaned;
            }
        }
        return $this->payload;
    }

    public function setPayload(string $payload): void
    {
        $this->payload = $payload;
    }


    /**
     * 解码SEND包
     */
    public static function decode(Framer $framer, string $data, int $version): self
    {
        $decoder = new Decoder($data);
        $packet = new self();
        
        // 复制framer属性
        $packet->setRemainingLength($framer->getRemainingLength());
        $packet->setNoPersist($framer->getNoPersist());
        $packet->setRedDot($framer->getRedDot());
        $packet->setSyncOnce($framer->getSyncOnce());
        $packet->setDUP($framer->getDUP());
        $packet->setFrameSize($framer->getFrameSize());

        try {
            // 读取设置
            $settingValue = $decoder->uint8();
            $packet->setting = new Setting($settingValue);

            // 读取客户端序列号
            $packet->clientSeq = $decoder->uint32();

            // 读取客户端消息编号
            $packet->clientMsgNo = $decoder->string();

            // 版本5之前支持streamNo
            if ($version < 5 && $version >= 2 && $packet->setting->isSet(Setting::STREAM)) {
                $packet->streamNo = $decoder->string();
            }

            // 读取频道ID
            $packet->channelId = $decoder->string();

            // 读取频道类型
            $packet->channelType = $decoder->uint8();

            // 版本3开始支持过期时间
            if ($version >= 3) {
                $packet->expire = $decoder->uint32();
            }

            // 读取消息key
            $packet->msgKey = $decoder->string();

            // 如果设置了topic，读取topic
            if ($packet->setting->isSet(Setting::TOPIC)) {
                $packet->topic = $decoder->string();
            }

            // 读取payload
            $packet->payload = $decoder->binaryAll();

        } catch (\Exception $e) {
            throw new \RuntimeException('解码SEND包失败: ' . $e->getMessage(), 0, $e);
        }

        return $packet;
    }

    /**
     * 编码SEND包
     */
    public function encode(Encoder $encoder, int $version): void
    {
        $encoder->writeUint8($this->setting->toUint8());
        $encoder->writeUint32($this->clientSeq);
        $encoder->writeString($this->clientMsgNo);

        // 版本5之前支持streamNo
        if ($version < 5 && $version >= 2 && $this->setting->isSet(Setting::STREAM)) {
            $encoder->writeString($this->streamNo);
        }

        $encoder->writeString($this->channelId);
        $encoder->writeUint8($this->channelType);

        // 版本3开始支持过期时间
        if ($version >= 3) {
            $encoder->writeUint32($this->expire);
        }

        $encoder->writeString($this->msgKey);

        if ($this->setting->isSet(Setting::TOPIC)) {
            $encoder->writeString($this->topic);
        }

        $encoder->writeBytes($this->payload);
    }

    /**
     * 计算SEND包大小
     */
    public function size(int $version): int
    {
        $size = 0;
        $size += 1; // Setting
        $size += 4; // ClientSeq
        $size += 2 + strlen($this->clientMsgNo); // ClientMsgNo

        // 版本5之前支持streamNo
        if ($version < 5 && $version >= 2 && $this->setting->isSet(Setting::STREAM)) {
            $size += 2 + strlen($this->streamNo); // StreamNo
        }

        $size += 2 + strlen($this->channelId); // ChannelId
        $size += 1; // ChannelType

        // 版本3开始支持过期时间
        if ($version >= 3) {
            $size += 4; // Expire
        }

        $size += 2 + strlen($this->msgKey); // MsgKey

        if ($this->setting->isSet(Setting::TOPIC)) {
            $size += 2 + strlen($this->topic); // Topic
        }

        $size += strlen($this->payload); // Payload

        return $size;
    }
}